package util;

import com.google.common.collect.Maps;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.Collection;
import java.util.Map;

/**
 * Protostuff序列化, 支持任意形式的java对象
 * @author zhangweilin
 * @date 2022/5/17 10:42
 * @Description:
 */
@Slf4j
public class ProtostuffSerializeUtil implements SerializeService {

	private static final Map<Class<?>, RuntimeSchema<?>> CLASS_TO_SCHEMA = Maps.newConcurrentMap();

	/**
	 * 序列化/反序列化包装类 Schema 对象， 用于解决复杂对象，如集合，map，数组等情况
	 */
	private static final Schema<SerializeDeserializeWrapper> WRAPPER_SCHEMA = RuntimeSchema.createFrom(SerializeDeserializeWrapper.class);

	/**
	 * 获取RuntimeSchema
	 * 此方法虽然是线程不安全的，但是由于{@link RuntimeSchema#createFrom(Class)}方法生成的对象对于相同的class是一致的，因此不需要加锁
	 *
	 * @param tClass 目标对象的Class对象
	 * @return 相应的RuntimeSchema
	 */
	@SuppressWarnings("unchecked")
	private static <T> RuntimeSchema<T> getRuntimeSchema(Class<T> tClass) {
		return ((RuntimeSchema<T>) CLASS_TO_SCHEMA.computeIfAbsent(tClass, RuntimeSchema::createFrom));
	}

	/**
	 * 获取序列化方式
	 *
	 * @return 响应结果
	 */
	@Override
	public String getSerialization() {
		return "protostuff";
	}

	/**
	 * 序列化为字节数组
	 *
	 * @param target 目标对象
	 * @return 字节数组
	 */
	@Override
	public <T> byte[] serializeToBytes(T target) {
		Class<T> clazz = (Class<T>) target.getClass();
		LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
		try {
			Object serializeObject = target;
			Schema schema = WRAPPER_SCHEMA;
			//集合
			if (Collection.class.isAssignableFrom(clazz)||Map.class.isAssignableFrom(clazz)||clazz.isArray()) {
				SerializeDeserializeWrapper wrapper = SerializeDeserializeWrapper.builder(
						target);
				serializeObject = wrapper;
				//数组
			}
			//如果数组情况，不做任何处理，去掉前面if的clazz.isArray()， 能让jdk crash,先不删除此处，后续深入研究
//			else if (clazz.isArray()) {

//			}
			else{
				schema = getRuntimeSchema(clazz);
			}
			return ProtostuffIOUtil.toByteArray(serializeObject, schema, buffer);
		} catch (Exception e) {
			LogUtil.error(log, e, "序列化对象异常 【{}]", target, e);
			throw new IllegalStateException(e.getMessage(), e);
		} finally {
			buffer.clear();
		}
	}

	/**
	 * 通过字节数组反序列化为对象
	 *
	 * @param bytes       字节数组
	 * @param targetClass 目标对象的类对象,如果是集合类型，请直接传外层的集合class过来，如果是List<zwl>,请传List.class即可
	 * @return 目标对象的实际结果
	 */
	@Override
	public <T> T deserializeWithBytes(byte[] bytes, Class<T> targetClass) {
		if (null == bytes) {
			return null;
		}
		try {
			//如果是集合或数组
			if (Collection.class.isAssignableFrom(targetClass) || Map.class.isAssignableFrom(targetClass) || targetClass.isArray()) {
				SerializeDeserializeWrapper<T> wrapper = new SerializeDeserializeWrapper<>();
				ProtostuffIOUtil.mergeFrom(bytes, wrapper, WRAPPER_SCHEMA);
				return wrapper.getData();
			} else {
				T message = targetClass.newInstance();
				Schema<T> schema = getRuntimeSchema(targetClass);
				ProtostuffIOUtil.mergeFrom(bytes, message, schema);
				return message;
			}
		} catch (Exception e) {
			LogUtil.error(log, e, "反序列化对象异常 【{}】", targetClass.getName(), e);
			throw new IllegalStateException(e.getMessage(), e);
		}
	}

	@Data
	public static class SerializeDeserializeWrapper<T> {

		private T data;

		public static <T> SerializeDeserializeWrapper<T> builder(T data) {
			SerializeDeserializeWrapper<T> wrapper = new SerializeDeserializeWrapper<>();
			wrapper.setData(data);
			return wrapper;
		}
	}
}